/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.formmetadata.event;

import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import com.inspur.edp.lcm.metadata.spi.event.MetadataEventArgs;
import com.inspur.edp.lcm.metadata.spi.event.MetadataEventListener;
import com.inspur.edp.web.common.entity.TerminalType;
import com.inspur.edp.web.common.io.FileUtility;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.metadata.MetadataGetterParameter;
import com.inspur.edp.web.common.metadata.MetadataTypeEnum;
import com.inspur.edp.web.common.metadata.MetadataUtility;
import com.inspur.edp.web.common.serialize.SerializeUtility;
import com.inspur.edp.web.formmetadata.entity.ResourceMetadataType;
import com.inspur.edp.web.formmetadata.metadata.FormMetadataContent;
import com.inspur.edp.web.formmetadata.metadata.formdom.FormDOM;
import com.inspur.edp.web.formmetadata.service.FormMetataService;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

import static com.inspur.edp.web.common.entity.TerminalType.MOBILE;

/**
 * @author guozhiqi
 */
public class FormMetadataSaveEventListener implements MetadataEventListener {

    /**
     * 元数据保存前事件
     */
    @Override
    public void fireMetadataSavingEvent(MetadataEventArgs metadataEventArgs) {

    }

    /**
     * 元数据保存后事件
     */
    @Override
    public void fireMetadataSavedEvent(MetadataEventArgs metadataEventArgs) {
        if (metadataEventArgs == null || metadataEventArgs.getMetadata() == null
                || metadataEventArgs.getMetadata().getHeader() == null) {
            return;
        }


        if (TerminalType.PC.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            // 同步本地语言包
            SyncLocalLanguagePackage(metadataEventArgs.getMetadata());
        }
    }

    private void SyncLocalLanguagePackage(GspMetadata formMetadata) {
        FormMetataService.exportLanguagePackage(formMetadata);
    }

    @Override
    public void fireMetadataDeletingEvent(MetadataEventArgs metadataEventArgs) {
        if (metadataEventArgs == null || metadataEventArgs.getMetadata() == null
                || metadataEventArgs.getMetadata().getHeader() == null) {
            return;
        }


        RelatedMetadataDeleteWhenFormMetadataDeleting relatedMetadataDeleteWhenFormMetadataDeleting = new RelatedMetadataDeleteWhenFormMetadataDeleting(metadataEventArgs);
        relatedMetadataDeleteWhenFormMetadataDeleting.computeIfPresent(this::isPcOrMobileForm, relatedMetadataDeleteWhenFormMetadataDeleting::deleteRelatedMetadata);
    }


    @Override
    public void fireMetadataDeletedEvent(MetadataEventArgs metadataEventArgs) {
        if (metadataEventArgs == null || metadataEventArgs.getMetadata() == null
                || metadataEventArgs.getMetadata().getHeader() == null) {
            return;
        }

        // 如果删除的是webcmp  那么在删除之后将对应的ts文件进行删除
        if ("WebComponent".equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            RelateTsFileDeleteWhenWebComponentDeleted relateTsFileDeleteWhenWebComponentDeleted = new RelateTsFileDeleteWhenWebComponentDeleted(metadataEventArgs);
            relateTsFileDeleteWhenWebComponentDeleted.deleteTsFileAfterWebComponentDeleted();
        }

        if (this.isPcOrMobileForm(metadataEventArgs)) {
            TerminalType terminalType = TerminalType.fromMetadataType(metadataEventArgs.getMetadata().getHeader().getType());
            RelateMetadataDeleteWhenFormMetadataDeleted relateMetadataDeleteWhenFormMetadataDeleted = new RelateMetadataDeleteWhenFormMetadataDeleted(metadataEventArgs);
            relateMetadataDeleteWhenFormMetadataDeleted.deleteRelateMetadataAfterFormMetadataDeleted((metadataCode) -> relateMetadataDeleteWhenFormMetadataDeleted.generateDeleteList(metadataCode, terminalType));

            //移动删除状态机
            if (TerminalType.MOBILE.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType())) {
                String formMetadataRelativePath = metadataEventArgs.getMetadata().getRelativePath();
                MetadataProjectService metadataProjectService = SpringBeanUtils.getBean(MetadataProjectService.class);
                MetadataProject projInfo = metadataProjectService.getMetadataProjInfo(formMetadataRelativePath);
                String projectPath = projInfo.getProjectPath();
                if (metadataEventArgs.getMetadata().getContent() != null) {

                    try {
                        String formMetadataStr = ((FormMetadataContent) metadataEventArgs.getMetadata().getContent()).getContents().toString();
                        FormDOM formDOM = SerializeUtility.getInstance().deserialize(formMetadataStr, FormDOM.class);
                        deleteRelateStateMachinesMetadata(projectPath, formDOM, metadataEventArgs.getMetadata());
                    } catch (RuntimeException ex) {
                        WebLogger.Instance.error(ex);
                    }
                }

            }
        }

        // 如果是帮助元数据的删除 那么将其关联的res也要对应的删除
        if ("HelpMetadata".equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            try {
                RelateMetadataDeleteWhenHelpMetadataDeleted relateMetadataDeleteWhenHelpMetadataDeleted = new RelateMetadataDeleteWhenHelpMetadataDeleted(metadataEventArgs);
                relateMetadataDeleteWhenHelpMetadataDeleted.deleteRelateHelpMetadata();
            } catch (Exception ex) {
                WebLogger.Instance.error(ex);
            }
        }
    }

    /**
     * 删除移动表单关联的sm元数据
     *
     * @param projectPath
     * @param formDOM
     */
    private void deleteRelateStateMachinesMetadata(String projectPath, FormDOM formDOM, GspMetadata sourceFormMetadata) {
        List<HashMap<String, Object>> stateMachineList = formDOM.getModule().getStateMachines();
        if (stateMachineList != null && stateMachineList.size() > 0) {
            for (HashMap<String, Object> stateMachine : stateMachineList) {
                if (stateMachine.containsKey("uri") && stateMachine.get("uri") != null) {
                    String stateMachineId = stateMachine.get("uri").toString();
                    MetadataGetterParameter metadataGetterParameter = MetadataGetterParameter.getNewInstance(stateMachineId, projectPath, MetadataTypeEnum.StateMachine);
                    metadataGetterParameter.setSourceMetadata(sourceFormMetadata, MetadataTypeEnum.Frm);
                    GspMetadata stateMachinesMetadata = MetadataUtility.getInstance().getMetadataWithDesign(metadataGetterParameter);
                    if (stateMachinesMetadata != null) {
                        // 删除状态机
                        MetadataUtility.getInstance().deleteMetadataWithDesign(stateMachinesMetadata.getRelativePath(), stateMachinesMetadata.getHeader().getFileName());
                    }
                }

            }
        }
    }


    /**
     * 判断是否是移动表单或PC表单类型
     *
     * @param metadataEventArgs
     * @return
     */
    private boolean isPcOrMobileForm(MetadataEventArgs metadataEventArgs) {
        return TerminalType.PC.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType()) ||
                TerminalType.MOBILE.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType());
    }

    /**
     * 表单元数据删除后将关联的文件进行删除
     *
     * @author guozhiqi
     */
    private class RelateMetadataDeleteWhenFormMetadataDeleted {
        private MetadataEventArgs metadataEventArgs;

        public RelateMetadataDeleteWhenFormMetadataDeleted(MetadataEventArgs metadataEventArgs) {
            this.metadataEventArgs = metadataEventArgs;
        }

        /**
         * 构造待删除的文件列表
         *
         * @param metaDataCode
         * @param terminalType
         * @return
         */
        public List<String> generateDeleteList(String metaDataCode, TerminalType terminalType) {
            List<String> deleteList = new ArrayList<>();
            String frmDotPrefix = metaDataCode + terminalType.getFormMetadataSuffix();
            // .frm 替换成为  _frm
            String frmXiaPrefix = metaDataCode + terminalType.getFormMetadataSuffix().replace(".", "_");

            // 表单关联资源文件
            deleteList.add(frmDotPrefix + ResourceMetadataType.EN.getSuffix());
            deleteList.add(frmDotPrefix + ResourceMetadataType.ZH_CH.getSuffix());
            deleteList.add(frmDotPrefix + ResourceMetadataType.ZH_CHT.getSuffix());

            // 表单元数据json文件
            deleteList.add(frmXiaPrefix + ".json");

            // 表单关联状态机
            deleteList.add(frmXiaPrefix + ".sm");

            // 表单元数据 关联VO 及其资源元数据
            deleteList.add(frmXiaPrefix + ".vo");
            deleteList.add(frmXiaPrefix + ".vo.en.lres");
            deleteList.add(frmXiaPrefix + ".vo.res");
            deleteList.add(frmXiaPrefix + ".vo.zh-CHT.lres");


            return deleteList;
        }

        /**
         * 表单元数据删除后关联元数据删除
         */
        public void deleteRelateMetadataAfterFormMetadataDeleted(Function<String, List<String>> relateMetadataDeleteListAction) {
            if (relateMetadataDeleteListAction == null) {
                return;
            }
            try {
                // 获取表单元数据的文件名称
                String metaDataCode = this.metadataEventArgs.getMetadata().getHeader().getCode();
                String formMetadataRelativePath = this.metadataEventArgs.getMetadata().getRelativePath();

                //获取待删除的文件列表 仅删除当前目录下
                List<String> relativeMetadataDeleteList = relateMetadataDeleteListAction.apply(metaDataCode);
                relativeMetadataDeleteList.forEach(t -> {
                    File deleteFileInfo = new File(FileUtility.combine(formMetadataRelativePath, t));
                    if (deleteFileInfo.exists()) {
                        // 执行元数据删除
                        MetadataUtility.getInstance().deleteMetadataWithDesign(formMetadataRelativePath, t);
                    }
                });
            } catch (Exception ex) {
                WebLogger.Instance.error(ex);
            }
        }
    }

    /**
     * Web Component 级联删除
     */
    private class RelateTsFileDeleteWhenWebComponentDeleted {
        private MetadataEventArgs metadataEventArgs;

        public RelateTsFileDeleteWhenWebComponentDeleted(MetadataEventArgs metadataEventArgs) {
            this.metadataEventArgs = metadataEventArgs;
        }

        /**
         * WebComponent 删除后删除对应的ts文件
         */
        private void deleteTsFileAfterWebComponentDeleted() {
            String tsFileName = this.metadataEventArgs.getMetadata().getHeader().getCode() + ".ts";
            String tsFilePath = this.metadataEventArgs.getMetadata().getRelativePath();
            String tsFileNameAndPath = FileUtility.combine(tsFilePath, tsFileName);
            try {
                FileUtility.deleteFile(tsFileNameAndPath);
            } catch (Exception ex) {
                WebLogger.Instance.error(ex.getMessage() + ex.getStackTrace());
            }
        }
    }


    /**
     * 帮助元数据级联删除
     */
    private class RelateMetadataDeleteWhenHelpMetadataDeleted {
        private MetadataEventArgs metadataEventArgs;

        public RelateMetadataDeleteWhenHelpMetadataDeleted(MetadataEventArgs metadataEventArgs) {
            this.metadataEventArgs = metadataEventArgs;
        }

        /**
         * 删除帮助关联的元数据
         */
        private void deleteRelateHelpMetadata() {
            String fileName = this.metadataEventArgs.getMetadata().getHeader().getFileName();
            String name = this.metadataEventArgs.getMetadata().getHeader().getCode();
            String relativePath = this.metadataEventArgs.getMetadata().getRelativePath();
            String projectRelativePath = MetadataUtility.getInstance().getMetadataProjectPath(relativePath);

            String relateResource = ResourceMetadataType.ZH_CH.getFileName(fileName);
            MetadataUtility.getInstance().deleteMetadataWithDesign(relativePath, relateResource);

            String relateEnResource = ResourceMetadataType.EN.getFileName(fileName);
            MetadataUtility.getInstance().deleteMetadataWithDesign(relativePath, relateEnResource);

            String relateZh_CHTResource = ResourceMetadataType.ZH_CHT.getFileName(fileName);
            MetadataUtility.getInstance().deleteMetadataWithDesign(relativePath, relateZh_CHTResource);

            // 删除帮助eapi
            String eapiName = name + "_hlp.eapi";
            String eapiFilePath = FileUtility.combine(projectRelativePath, "eapi");
            MetadataUtility.getInstance().deleteMetadataWithDesign(eapiFilePath, eapiName);

            // 删除导航帮助  nav eapi
            String navEapiName = name + "_hlp_nav.eapi";
            String navEapiFilePath = FileUtility.combine(projectRelativePath, "eapi");
            MetadataUtility.getInstance().deleteMetadataWithDesign(navEapiFilePath, navEapiName);

            // 删除导航帮助 nav
            String relateNavVO = name + "_hlp_nav.vo";
            deleteHelpVoAndRelateMetadata(relateNavVO, relativePath);

            // 删除普通帮助
            String relateVO = name + "_hlp.vo";
            deleteHelpVoAndRelateMetadata(relateVO, relativePath);
        }

        /**
         * 删除帮助及其关联资源元数据
         *
         * @param relateVO
         * @param relativePath
         */
        private void deleteHelpVoAndRelateMetadata(String relateVO, String relativePath) {
            MetadataUtility.getInstance().deleteMetadataWithDesign(relativePath, relateVO);

            String relateVORes = ResourceMetadataType.ZH_CH.getFileName(relateVO);
            MetadataUtility.getInstance().deleteMetadataWithDesign(relativePath, relateVORes);

            String relateVOEnRes = ResourceMetadataType.EN.getFileName(relateVO);
            MetadataUtility.getInstance().deleteMetadataWithDesign(relativePath, relateVOEnRes);

            String relateVOZHCHTRes = ResourceMetadataType.ZH_CHT.getFileName(relateVO);
            MetadataUtility.getInstance().deleteMetadataWithDesign(relativePath, relateVOZHCHTRes);
        }
    }

    /**
     * 表单元数据删除 关联删除对应的元数据
     * 内部类 对外无需可见
     */
    private class RelatedMetadataDeleteWhenFormMetadataDeleting {


        private MetadataEventArgs metadataEventArgs;

        public RelatedMetadataDeleteWhenFormMetadataDeleting(MetadataEventArgs metadataEventArgs) {
            this.metadataEventArgs = metadataEventArgs;
        }

        /**
         * 如果允许删除 那么执行关联元数据删除操作
         *
         * @param predicateCanDelete
         * @param deleteRelateMetadataAction
         */
        public void computeIfPresent(Predicate<MetadataEventArgs> predicateCanDelete, Consumer<MetadataEventArgs> deleteRelateMetadataAction) {
            if (deleteRelateMetadataAction != null && predicateCanDelete.test(this.metadataEventArgs)) {
                deleteRelateMetadataAction.accept(this.metadataEventArgs);
            }
        }

        /**
         * 删除关联元数据
         *
         * @param metadataEventArgs
         */
        public void deleteRelatedMetadata(MetadataEventArgs metadataEventArgs) {
            try {
                // 获取表单元数据的文件名称
                String metaDataCode = metadataEventArgs.getMetadata().getHeader().getCode();
                String formMetadataRelativePath = metadataEventArgs.getMetadata().getRelativePath();

                MetadataProjectService metadataProjectService = SpringBeanUtils.getBean(MetadataProjectService.class);
                MetadataProject projInfo = metadataProjectService.getMetadataProjInfo(formMetadataRelativePath);
                String projectPath = projInfo.getProjectPath();

                //获取待删除的文件列表 仅删除当前目录下
                List<String> relativeMetadataDeleteList = new ArrayList<>(); //generateDeleteList(metaDataCode, new File(formMetadataRelativePath));

                // 读取表单元数据  解析其中的eapiid
                // 如果传递的表单元数据中内容为空
                if (metadataEventArgs.getMetadata().getContent() == null) {
                    GspMetadata metadata = MetadataUtility.getInstance().getMetadataWithDesign(metadataEventArgs.getMetadata().getHeader().getFileName(), metadataEventArgs.getMetadata().getRelativePath());
                    if (metadata != null) {
                        metadataEventArgs.getMetadata().setContent(metadata.getContent());
                    }
                }

                if (metadataEventArgs.getMetadata().getContent() != null) {
                    String formMetadataStr = ((FormMetadataContent) metadataEventArgs.getMetadata().getContent()).getContents().toString();
                    FormDOM formDOM = SerializeUtility.getInstance().deserialize(formMetadataStr, FormDOM.class);
                    if (formDOM.getModule().getSchemas() != null && formDOM.getModule().getSchemas().size() > 0) {
                        deleteRelateEapiMetadata(projectPath, formDOM, metadataEventArgs.getMetadata());
                    }

                    // 获取待删除的命令元数据  命令构件 ts文件
                    try {
                        WebCommandMetadataDelete webCommandMetadataDelete = new WebCommandMetadataDelete();
                        List<String> webCmpAndTsDeleteList = webCommandMetadataDelete.generateWebCmpAndTsDeleteList(formDOM, projInfo);
                        relativeMetadataDeleteList.addAll(new ArrayList<>(webCmpAndTsDeleteList));
                    } catch (Exception ex) {
                        WebLogger.Instance.error(ex);
                    }
                }

                relativeMetadataDeleteList.forEach(t -> {
                    File deleteFileInfo = new File(FileUtility.combine(formMetadataRelativePath, t));
                    if (deleteFileInfo.exists()) {
                        // 执行元数据删除
                        MetadataUtility.getInstance().deleteMetadataWithDesign(formMetadataRelativePath, t);
                    }
                });

                // 删除关联的表单源代码
                deleteRelateFormSourceCode(projectPath, projInfo.getName(), metaDataCode);
                // 更新页面流元数据
            } catch (RuntimeException ex) {
                WebLogger.Instance.error(ex);
            } catch (Exception ex) {
                WebLogger.Instance.error(ex);
            }
        }

        /**
         * 删除关联的eapi元数据
         *
         * @param projectPath
         * @param formDOM
         */
        private void deleteRelateEapiMetadata(String projectPath, FormDOM formDOM, GspMetadata sourceFormMetadata) {
            Object objEapiId = formDOM.getModule().getSchemas().get(0).get("eapiId");
            if (objEapiId != null) {
                String strEapiId = objEapiId.toString();

                MetadataGetterParameter metadataGetterParameter = MetadataGetterParameter.getNewInstance(strEapiId, projectPath, MetadataTypeEnum.Eapi);
                metadataGetterParameter.setSourceMetadata(sourceFormMetadata, MetadataTypeEnum.Frm);
                GspMetadata eapiMetadata = MetadataUtility.getInstance().getMetadataWithDesign(metadataGetterParameter);
                if (eapiMetadata != null) {
                    // eapi 单独删除
                    MetadataUtility.getInstance().deleteMetadataWithDesign(eapiMetadata.getRelativePath(), eapiMetadata.getHeader().getFileName());
                }
            }
        }


        /**
         * 删除关联的源代码目录文件
         *
         * @param projectPath
         * @param projectName
         * @param formCode
         */
        private void deleteRelateFormSourceCode(String projectPath, String projectName, String formCode) {
            String sourceCodeSrcPath = FileUtility.combineOptional(projectPath, "src", "app", "projects", projectName.toLowerCase(), "src");
            String sourceCodeFormPath = FileUtility.combine(sourceCodeSrcPath, "app", formCode.toLowerCase());
            if (FileUtility.exists(sourceCodeFormPath)) {
                FileUtility.deleteFolder(sourceCodeFormPath);
            }
        }
    }

}

